#include "updatesocket.h"

UpdateSocket::UpdateSocket(int socketId, int state, QObject *parent)
    : QTcpSocket(parent)
{
    m_receSize = 0;
    this->setSocketDescriptor(socketId);
    init();
}


void UpdateSocket::init()
{

    ReceInfoStruct receInfoStruct;
    this->initReceInfoStruct(receInfoStruct);
    m_receFileInfoList.append(receInfoStruct);

    m_curInfoNum = 0;
    connect(this, SIGNAL(readyRead()),      this, SLOT(receDataFromServer()));
    connect(this, SIGNAL(disconnected()),   this, SLOT(disConnectSlot()));


    this->sendConnectSuccess();
}

/*******************************************
  功能：初始化包管理类
  参数：
  返回值：
  ******************************************/
ReceInfoStruct& UpdateSocket::initReceInfoStruct(ReceInfoStruct &receInfoStruct)
{
    receInfoStruct.hasReceLen = 0;
    receInfoStruct.fileLen = 20;
    receInfoStruct.file = new QFile(this);
    receInfoStruct.recePkgmange = new RecePkgManage(this);
    receInfoStruct.isFinish = false;
    receInfoStruct.pkgCount = 0;
    receInfoStruct.enmErr = TCPNOERR;
    receInfoStruct.tryDLCount = 0;
    return receInfoStruct;
}

/*******************************************
  功能：发送连接成功至维护终端
  参数：
  返回值：
  ******************************************/
void UpdateSocket::sendConnectSuccess()
{
    PIDS_SendState  pids_SendState;
    int sendSize;

    pids_SendState.BaseFiles.PriCmd = PRICMD_UPDATEPIDS;
    pids_SendState.BaseFiles.SubCmd = SUBCMD_CONNECTSUCCESS;


    int len = sizeof(PIDS_SendState );
    int plen = 6 + len;
    UBYTE *pCode = new UBYTE[plen];
    pCode[0]= (quint8)0x3C;
    pCode[1]= (quint8)0x21;
    pCode[2]= (quint8)(len>>8);
    pCode[3]= (quint8)(len%256);
    memcpy(pCode+4, &pids_SendState, len);

    pCode[len+4] = (quint8)0x21;
    pCode[len+5] = (quint8)0x3E;
    sendSize = this->writeData(pCode, plen);
    free (pCode);
}

/*******************************************
  功能：处理发送过来的数据
  参数：
  返回值：
  ******************************************/
void UpdateSocket::processReceData(const QByteArray &receData)
{
    QByteArray receBytes = receData;

    qint16 header, len, eof;

    if (!m_byteArray.isEmpty())
    {
        receBytes.insert(0, m_byteArray);
        m_byteArray.clear();
    }

    while(!receBytes.isEmpty())
    {        

        QDataStream receStream(&receBytes, QIODevice::ReadOnly);
        receStream.setVersion(QDataStream::Qt_4_6);

        receStream >> header >> len;
        receStream.skipRawData(len);
        receStream >> eof;

        if (header != quint16(0x3c21))
        {
//            qDebug() << "header != quint16(0x3c21)";
            receBytes.remove(0, 1);
        } else {
            if (eof == quint16(0x213E))
            {
                if (len > 200)
                {
                    PIDS_FilePkg       PIDS_filePkg;
                    memcpy(&PIDS_filePkg, receBytes.data()+4, (sizeof(PIDS_FilePkg)));
                    this->writeDataToFile(PIDS_filePkg);

                    receBytes.remove(0, len+6);
                } else {
                    PIDS_SendFileFish pids_sendFileFinish;

                    memcpy(&pids_sendFileFinish, receBytes.data()+4, sizeof(PIDS_SendFileFish));

                    if (pids_sendFileFinish.BaseFiles.SubCmd == SUBCMD_CURFILEFINISH)
                    {
                        this->receFileFinish(pids_sendFileFinish);
                    } else {
                        if (this->checkUnfinishFile())
                        {
                            this->sendUpdateSuccess();
                            receUpdateState(1);
                        } else {
                            this->sendUpdateErr(m_updateInfo.parsexmlState);
                        }
                    }
                    receBytes.remove(0, len+6);
                }
            } else {
                if (receBytes.size() > (1200))
                {
                    receBytes.remove(0, 1);
                } else {
                    m_byteArray = receBytes;
                    receBytes.clear();
                    return;
                }
            }
        }
    }
}

/*******************************************
  功能：将数据写进文件
  参数：
  返回值：
  ******************************************/
inline void UpdateSocket::writeDataToFile(PIDS_FilePkg& filePkg)
{
    int nWrite;

    if (checkSum(filePkg.pkg, filePkg.pkgSize) != filePkg.checkSum)
    {
        return;
    }

    quint64 hasReceLen;

    QFileInfo fileInfo(filePkg.fileName);

    if (m_receFileInfoList[m_curInfoNum].fileName != filePkg.fileName)
    {
        bool hasFind = false;
        for(int i = 0; i < m_receFileInfoList.count(); i++)
        {
            if (m_receFileInfoList[i].fileName == filePkg.fileName)
            {
                m_curInfoNum = i;
                hasFind = true;
                break;
            }
        }
        if (!hasFind)
        {
            if (!m_receFileInfoList[m_curInfoNum].file->isOpen())
            {
                QString filePath = tr("%1/%2/%3").arg(g_installPath).arg(UPDATEPATH).arg(filePkg.fileName);
                QFileInfo info(filePath);

                QDir dir(info.absolutePath());
                if (!dir.exists())
                {
                    dir.mkpath(info.path());
                }

                m_receFileInfoList[m_curInfoNum].file->setFileName(filePath);
                m_receFileInfoList[m_curInfoNum].file->open(QIODevice::ReadWrite);
                m_receFileInfoList[m_curInfoNum].fileName = filePkg.fileName;
            } else {
                ReceInfoStruct receInfoStruct;
                receInfoStruct = this->initReceInfoStruct(receInfoStruct);
                m_receFileInfoList.append(receInfoStruct);

                m_curInfoNum = m_receFileInfoList.count() - 1;

                QString filePath = tr("%1/%2/%3").arg(g_installPath).arg(UPDATEPATH).arg(fileInfo.fileName());

//                qDebug() << "filePath=====================>>" << filePath;
                QFileInfo info(filePath);
                QDir dir(info.absolutePath());
                if (!dir.exists())
                {
                    dir.mkpath(info.path());
                }


                m_receFileInfoList[m_curInfoNum].file->setFileName(filePath);
                m_receFileInfoList[m_curInfoNum].file->open(QIODevice::WriteOnly);
                m_receFileInfoList[m_curInfoNum].fileName = filePkg.fileName;
            }
        }
    }
        m_receFileInfoList[m_curInfoNum].recePkgmange->initRecePkgManage(filePkg.pkgCount);

        //将每一次的数据写进文件中，当收到数据 >= 文件长度，则证明文件读取完成
        m_receFileInfoList[m_curInfoNum].pkgCount = filePkg.pkgCount;

        m_receFileInfoList[m_curInfoNum].file->seek(filePkg.position);
        m_receFileInfoList[m_curInfoNum].fileLen = filePkg.fileLen;

        nWrite = m_receFileInfoList[m_curInfoNum].file->write(filePkg.pkg, filePkg.pkgSize);
        m_receFileInfoList[m_curInfoNum].recePkgmange->addRecePkg(filePkg.pkgNum, m_receFileInfoList[m_curInfoNum].file->pos());
        hasReceLen = m_receFileInfoList[m_curInfoNum].hasReceLen + filePkg.pkgSize;
        m_receFileInfoList[m_curInfoNum].hasReceLen = hasReceLen;
}

/*******************************************
  功能：维护终端已经发送完所有文件，此时检测本地文件是否已经
       下载完成，是否有丢包等错误出现
  参数：
  返回值：
  ******************************************/
void UpdateSocket::receFileFinish(const PIDS_SendFileFish& fileFinish)
{
    bool hasFind = false;
    int receInfoNum = 0;
    for(int i = 0; i < m_receFileInfoList.count(); i++)
    {
        if (m_receFileInfoList[i].fileName == fileFinish.fileName)
        {
            receInfoNum = i;
            hasFind = true;
            break;
        }
    }

    if (hasFind)
    {
        if (receInfoNum < m_curInfoNum)
        {
            m_curInfoNum--;
        }
        if (m_curInfoNum < 0)
        {
            ReceInfoStruct receInfoStruct;
            this->initReceInfoStruct(receInfoStruct);
            m_receFileInfoList.append(receInfoStruct);

            m_curInfoNum = 0;
        }

        if (m_receFileInfoList[receInfoNum].recePkgmange->getunRecePkgList().count() == 0 && m_receFileInfoList[receInfoNum].hasReceLen >= m_receFileInfoList[receInfoNum].fileLen)
//        if (m_receFileInfoList[receInfoNum].recePkgmange->getunRecePkgList().count() == 0 )

        {         
            if (m_receFileInfoList[receInfoNum].fileName == "")
            {
                return;
            }

            QByteArray fileByteArray;
            QByteArray md5QByteArray;


            m_receFileInfoList[receInfoNum].file->flush();
            m_receFileInfoList[receInfoNum].file->seek(0);

            if (m_receFileInfoList[receInfoNum].file->size() > 64 * 1024)
            {
                QCryptographicHash hash(QCryptographicHash::Md5);
                while(!m_receFileInfoList[receInfoNum].file->atEnd()){
                    QByteArray data_md5_file= m_receFileInfoList[receInfoNum].file->read(64*1024);
                    hash.addData(data_md5_file);
                }
                md5QByteArray = hash.result();
            } else {
                fileByteArray = m_receFileInfoList[receInfoNum].file->readAll();
                md5QByteArray = QCryptographicHash::hash(fileByteArray,QCryptographicHash::Md5);
            }

//            fileByteArray = m_receFileInfoList[receInfoNum].file->readAll();
//            md5QByteArray = QCryptographicHash::hash(fileByteArray,QCryptographicHash::Md5);

            QString localMD5Str = md5QByteArray.toHex().constData();
            QString clientMD5Str =  fileFinish.MD5checkSum;

//            qDebug() <<  m_receFileInfoList[receInfoNum].fileName << "localMD5Str====>" << localMD5Str << "clientMD5" << clientMD5Str;

            m_receFileInfoList[receInfoNum].file->close();

            if (localMD5Str == clientMD5Str)
            {
                m_hadDLFiles.append(m_receFileInfoList[receInfoNum].fileName);

                if (m_receFileInfoList[receInfoNum].fileName == VERSIONFILENAME)
                {
                    UpdateXmlParser updateXmlParser;
                    QString xmlPath = m_receFileInfoList[receInfoNum].file->fileName();
                    m_updateInfo = updateXmlParser.parser(xmlPath);
                }
            } else {
//                qDebug()  << m_receFileInfoList[receInfoNum].fileName << localMD5Str << "localMD5Str != clientMD5Str" << clientMD5Str;

            }

            delete m_receFileInfoList[receInfoNum].file;
            delete m_receFileInfoList[receInfoNum].recePkgmange;
            m_receFileInfoList[receInfoNum].isFinish = true;

            m_receFileInfoList.removeAt(receInfoNum);


        } else {
            m_hadDLFiles.append(m_receFileInfoList[receInfoNum].fileName);
//            this->applyLostFile(receInfoNum);
        }

        if (m_receFileInfoList.count() == 0 || m_receFileInfoList.at(m_receFileInfoList.count() - 1).fileName == "")
        {
            ReceInfoStruct receInfoStruct;
            receInfoStruct = this->initReceInfoStruct(receInfoStruct);
            m_receFileInfoList.append(receInfoStruct);
        }
          m_curInfoNum = m_receFileInfoList.count() - 1;

    }
}

/*******************************************
  功能：申请下载丢失的数据包
  参数：
  返回值：
  ******************************************/
void UpdateSocket::applyLostFile(const int receInfoNum)
{
    PIDS_AppLostPkg pids_appLostPkg;
    int sendSize;

    QList<FilePkgStruct> list = m_receFileInfoList[receInfoNum].recePkgmange->getunRecePkgList();
    int count = 0;

    for(int i = 0; i < list.count(); i++)
    {
        FilePkgStruct lostpkgStruct = list.at(i);
            count++;
//            if(count > 15)
//            {
//                int k = 0;
//                while(k < -5 && !this->waitForBytesWritten(10))
//                {
//                    k++;
//                }
//                count = 0;
//            }
            pids_appLostPkg.BaseFiles.PriCmd = PRICMD_UPDATEPIDS;
            pids_appLostPkg.BaseFiles.SubCmd = SUBCMD_APPLOSTPKG;
            strcpy(pids_appLostPkg.fileName, m_receFileInfoList[receInfoNum].fileName.toLatin1().data());
            pids_appLostPkg.startPkgNum = lostpkgStruct.start;
            pids_appLostPkg.endPkgNum = lostpkgStruct.end;
            pids_appLostPkg.position = lostpkgStruct.startPos;
            pids_appLostPkg.result = SUBCMD_APPLOSTPKG;


            int len = sizeof(pids_appLostPkg);
            int plen = 6 + len;
            UBYTE *pCode = new UBYTE[plen];
            pCode[0]= (quint8)0x3C;
            pCode[1]= (quint8)0x21;
            pCode[2]= (quint8)(len>>8);
            pCode[3]= (quint8)(len%256);
            memcpy(pCode+4, &pids_appLostPkg, len);

            pCode[len+4] = (quint8)0x21;
            pCode[len+5] = (quint8)0x3E;
            sendSize = this->writeData(pCode, plen);
            free (pCode);
    }
}


/*******************************************
  功能：申请下载文件
  参数：
  返回值：
  ******************************************/
void UpdateSocket::applyGetFile(const QString& fileName)
{
    PIDS_SendState  pids_SendState ;
    int sendSize;
    int len = sizeof(PIDS_SendState );
    int plen = 6 + len;

    pids_SendState .BaseFiles.PriCmd = PRICMD_UPDATEPIDS;
    pids_SendState .BaseFiles.SubCmd = SUBCMD_APPFILE;
    pids_SendState .result = 0;
    strcpy(pids_SendState .fileName, fileName.toLatin1().data());

    UBYTE *pCode = new UBYTE[plen];
    pCode[0]= (quint8)0x3C;
    pCode[1]= (quint8)0x21;
    pCode[2]= (quint8)(len>>8);
    pCode[3]= (quint8)(len%256);
    memcpy(pCode+4, &pids_SendState , len);

    pCode[len+4] = (quint8)0x21;
    pCode[len+5] = (quint8)0x3E;
    sendSize = this->writeData(pCode, plen);
    free (pCode);
}

/*******************************************
  功能：检查是不否有文件已经完成，但却丢失了结束包
  参数：
  返回值：
  ******************************************/
bool UpdateSocket::checkUnfinishFile()
{

    if (m_hadDLFiles.count() <= 0)
    {
        return false;
    }

    if (m_updateInfo.parsexmlState != SUBCMD_UPDATEXMLSUCCESS)
    {
        return false;
    }

    ReceInfoStruct receInfo;
    bool isFinish = true;

    for(int i = 0; i < m_receFileInfoList.count(); i++)
    {
        receInfo = m_receFileInfoList.at(i);
        if (receInfo.hasReceLen < receInfo.fileLen || receInfo.recePkgmange->getUnRecePkgListCount() != 0)
        {
           if (receInfo.fileName != "")
           {               
               this->applyLostFile(i);
               isFinish = false;
           }
        }
    }



    QStringList updateFileList;
    for(int i = 0; i < m_updateInfo.updateFileList.count(); i++)
    {
        updateFileList.append(m_updateInfo.updateFileList.at(i).fileName);
    }
    for(int i = 0; i < m_hadDLFiles.count(); i++)
    {

        if (updateFileList.contains(m_hadDLFiles.at(i)))
        {
            int index = updateFileList.indexOf(m_hadDLFiles.at(i));
            updateFileList.removeAt(index);
        }
    }

    if (!updateFileList.isEmpty())
    {
        for(int i = 0; i < updateFileList.count(); i++)
        {
            this->applyGetFile(updateFileList.at(i));
            isFinish = false;
        }
    }



    return isFinish;
}

/*******************************************
  功能：如果全部更新文件都发送完成并且无错误，那么发送完成数据给维护终端
  参数：
  返回值：
  ******************************************/
void UpdateSocket::sendUpdateSuccess()
{
//    qDebug() << "sendUpdateSuccess====================>>";
    PIDS_SendState  pids_SendState;
    int sendSize;

    pids_SendState.BaseFiles.PriCmd = PRICMD_UPDATEPIDS;
    pids_SendState.BaseFiles.SubCmd = SUBCMD_SUCCESS;


    int len = sizeof(PIDS_SendState );
    int plen = 6 + len;
    UBYTE *pCode = new UBYTE[plen];
    pCode[0]= (quint8)0x3C;
    pCode[1]= (quint8)0x21;
    pCode[2]= (quint8)(len>>8);
    pCode[3]= (quint8)(len%256);
    memcpy(pCode+4, &pids_SendState, len);

    pCode[len+4] = (quint8)0x21;
    pCode[len+5] = (quint8)0x3E;
    sendSize = this->writeData(pCode, plen);
    free (pCode);
}

void UpdateSocket::sendUpdateErr(quint8 err)
{
    PIDS_SendState  pids_SendState;
    int sendSize;

    if (err == SUBCMD_UPDATEXMLSUCCESS)
        return;

    pids_SendState.BaseFiles.PriCmd = PRICMD_UPDATEPIDS;

    pids_SendState.BaseFiles.SubCmd = err;

    int len = sizeof(PIDS_SendState);
    int plen = 6 + len;
    UBYTE *pCode = new UBYTE[plen];
    pCode[0]= (quint8)0x3C;
    pCode[1]= (quint8)0x21;
    pCode[2]= (quint8)(len>>8);
    pCode[3]= (quint8)(len%256);
    memcpy(pCode+4, &pids_SendState, len);

    pCode[len+4] = (quint8)0x21;
    pCode[len+5] = (quint8)0x3E;
    sendSize = this->writeData(pCode, plen);
    free (pCode);
}

/*******************************************
  功能：每个包的检验号检测
  参数：
  返回值：
  ******************************************/
inline unsigned short UpdateSocket::checkSum(UBYTE *buffer, int size)
{
    unsigned long   cksum = 0;
    unsigned short  answer = 0;

    while(size > 1)
    {
        cksum+=*buffer++;
        size -=sizeof(UBYTE);
    }

    if (size)
    {
        cksum += *(UBYTE*)buffer;
    }

    cksum   =   (cksum   >>   16)   +   (cksum   &   0xffff);
    cksum   +=   (cksum   >>   16);
    answer   =   ~cksum;

    return   (answer);
}


void UpdateSocket::receDataFromServer()
{
    if (this->bytesAvailable() < 7)
    {
        return;
    }

    m_receSize += this->bytesAvailable();

    QByteArray receBytes = this->readAll();
    this->processReceData(receBytes);
}

void UpdateSocket::receData()
{

}

/*******************************************
  功能：断开连接槽函数，如果断开连接，将发送一个断开信息，把当前服务端的线程关闭
  参数：
  返回值：
  ******************************************/
void UpdateSocket::disConnectSlot()
{
    this->disconnectFromHost();
    this->close();
    emit disconnectSignal();
    this->deleteLater();
}

/*******************************************
  功能:
  参数：
  返回值：
  ******************************************/
void UpdateSocket::receXmlParser(int err)
{
    if (err <= 0)
        return;

    switch(err)
    {
    case 1:
        break;
    case 2:
        break;
    case 3:
        break;
    case 4:
        break;
    default:
        break;
    }
}

//查找所有文件
QStringList findFiles(const QString& filePath)
{
    QDir dir(filePath);
    QFileInfoList fileInfoList = dir.entryInfoList();
    QStringList fileList;

    foreach(QFileInfo fileInfo, fileInfoList)
    {
        if(fileInfo.fileName() == "." || fileInfo.fileName() == "..")
            continue;

        //查找子目录
        if(fileInfo.isDir())
        {
            //递归调用拷贝
            QStringList list = findFiles(fileInfo.filePath());
            if (!list.isEmpty())
            {
                fileList.append(list);
            }
        }
        //查找子文件
        else
        {

        fileList.append(fileInfo.fileName());
        }
    }
    return fileList;
}

